Conversation
|
Caution Review failedThe pull request is closed. Walkthrough온보딩 UI와 파일 업로드 경로/요청 방식을 변경합니다. 버튼 색상 및 진행바 색상을 업데이트하고 MultiSelect에 선택 상태와 버튼 그리드를 도입했으며, SingleSelect 기본 옵션과 채팅 업로드 메시지를 수정하고 파일 업로드를 axios + 환경변수 URL로 전환했습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User (UI)
participant C as FileSendButton
participant S as UploadEndpoint (env VITE_UPLOAD_URL)
rect rgb(230,245,255)
note right of C: 새 흐름 (axios + env URL)
end
U->>C: 파일 선택 + 전송
C->>S: POST formData (axios.post(uploadUrl,...))
S-->>C: 200 OK (업로드 성공)
C-->>U: 업로드 성공 처리 / 메시지 표시
(참고: 이전 흐름은 내부 Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/onboarding/SingleSelect.tsx (1)
11-29: 선택된 값을 수집할 수 없는 구조입니다.현재
select요소가value와onChange속성 없이 비제어 컴포넌트로 구현되어 있어, 사용자가 선택한 값을 부모 컴포넌트(Onboarding)에서 가져올 수 없습니다. 온보딩 플로우가 완료되어도 실제 폼 데이터를 수집하거나 제출할 수 없게 됩니다.다음과 같이 제어 컴포넌트 패턴으로 수정하세요:
-const SingleSelect = ({ title }: { title: string }) => { +interface SingleSelectProps { + title: string; + value: string; + onChange: (value: string) => void; +} + +const SingleSelect = ({ title, value, onChange }: SingleSelectProps) => { return ( <div className="flex flex-col gap-6"> {/* 제목 */} <p className="text-[24px] font-semibold">{title} 선택하세요</p> {/* 단일 선택 버튼 */} - <select className="rounded-xl border border-gray-200 px-2 py-4 text-[20px] outline-none focus:border-gray-200"> + <select + value={value} + onChange={(e) => onChange(e.target.value)} + className="rounded-xl border border-gray-200 px-2 py-4 text-[20px] outline-none focus:border-gray-200" + > <option className="" value=""> 선택하세요 </option>src/pages/onboarding/Onboarding.tsx (1)
10-46: 온보딩 폼 데이터를 수집하고 관리하는 상태가 없습니다.현재 구조에서는 사용자가 각 단계에서 입력한 값들(업종, 업체명, 위치, 매출, 타겟층)을 저장하거나 수집할 방법이 없습니다.
handleComplete함수는 단순히 페이지만 이동하며, 실제 온보딩 데이터는 모두 손실됩니다. 이는 온보딩 기능을 완전히 무용지물로 만드는 치명적인 문제입니다.다음과 같이 폼 상태 관리를 추가하세요:
const Onboarding = () => { const navigate = useNavigate(); const [currentStep, setCurrentStep] = useState(1); const totalSteps = 6; + + // 폼 데이터 상태 + const [formData, setFormData] = useState({ + businessType: '', + businessName: '', + location: '', + averageRevenue: '', + targetRevenue: '', + targetAudience: [] as string[], + }); const handleComplete = () => { + // 폼 데이터 유효성 검사 + console.log('Submitting onboarding data:', formData); + // TODO: API 호출로 데이터 전송 navigate('/chat'); }; const renderStepContent = () => { switch (currentStep) { case 1: - return <SingleSelect key="step1" title="업종을" />; + return ( + <SingleSelect + key="step1" + title="업종을" + value={formData.businessType} + onChange={(value) => setFormData(prev => ({ ...prev, businessType: value }))} + /> + ); case 2: - return <TextInput key="step2" title="업체명을" placeholder="" />; + return ( + <TextInput + key="step2" + title="업체명을" + placeholder="" + value={formData.businessName} + onChange={(value) => setFormData(prev => ({ ...prev, businessName: value }))} + /> + ); // ... 나머지 케이스도 동일하게 수정이후
TextInput과PriceInput컴포넌트도SingleSelect,MultiSelect와 같이 제어 컴포넌트 패턴으로 수정해야 합니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/components/onboarding/FormButton.tsx(1 hunks)src/components/onboarding/MultiSelect.tsx(1 hunks)src/components/onboarding/SingleSelect.tsx(1 hunks)src/pages/chat/ChatPageTest.tsx(1 hunks)src/pages/onboarding/Onboarding.tsx(2 hunks)
🔇 Additional comments (4)
src/components/onboarding/SingleSelect.tsx (1)
2-8: 업종 옵션 데이터가 적절합니다.한국 사업자 유형에 맞는 카테고리로 잘 업데이트되었습니다.
src/pages/chat/ChatPageTest.tsx (1)
119-129: LGTM!파일 업로드 성공 메시지가 더 명확하고 사용자 친화적으로 개선되었습니다.
src/pages/onboarding/Onboarding.tsx (1)
13-13: 단계 수 증가 및 테마 색상 변경이 일관성 있게 적용되었습니다.6단계로 확장된 온보딩 플로우와
#0D2D84색상 테마가FormButton,MultiSelect등 다른 컴포넌트들과 일관되게 적용되어 있습니다.Also applies to: 61-61
src/components/onboarding/FormButton.tsx (1)
3-3: LGTM!버튼 색상이 새로운 테마 색상(
#0D2D84)으로 일관성 있게 업데이트되었습니다. 이는 진행 상황 표시줄 및 선택된 옵션의 색상과 일치합니다.
| interface MultiSelectProps { | ||
| title: string; | ||
| options?: string[]; | ||
| } | ||
|
|
||
| const MultiSelect = ({ | ||
| title, | ||
| options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'], | ||
| }: MultiSelectProps) => { | ||
| const [selectedOptions, setSelectedOptions] = useState<string[]>([]); | ||
|
|
||
| const toggleOption = (option: string) => { | ||
| setSelectedOptions((prev) => | ||
| prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option], | ||
| ); | ||
| }; |
There was a problem hiding this comment.
선택된 값을 부모 컴포넌트로 전달하는 방법이 없습니다.
selectedOptions 상태가 컴포넌트 내부에만 존재하고 부모로 노출되지 않아, Onboarding.tsx에서 사용자가 선택한 타겟층 데이터를 수집할 수 없습니다. 이는 폼 제출 시 필수 데이터가 누락되는 치명적인 문제입니다.
다음과 같이 onChange 콜백을 추가하세요:
interface MultiSelectProps {
title: string;
options?: string[];
+ value?: string[];
+ onChange?: (selected: string[]) => void;
}
const MultiSelect = ({
title,
options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'],
+ value = [],
+ onChange,
}: MultiSelectProps) => {
- const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
+ const selectedOptions = value;
const toggleOption = (option: string) => {
- setSelectedOptions((prev) =>
- prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option],
- );
+ const newSelection = selectedOptions.includes(option)
+ ? selectedOptions.filter((item) => item !== option)
+ : [...selectedOptions, option];
+ onChange?.(newSelection);
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| interface MultiSelectProps { | |
| title: string; | |
| options?: string[]; | |
| } | |
| const MultiSelect = ({ | |
| title, | |
| options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'], | |
| }: MultiSelectProps) => { | |
| const [selectedOptions, setSelectedOptions] = useState<string[]>([]); | |
| const toggleOption = (option: string) => { | |
| setSelectedOptions((prev) => | |
| prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option], | |
| ); | |
| }; | |
| interface MultiSelectProps { | |
| title: string; | |
| options?: string[]; | |
| value?: string[]; | |
| onChange?: (selected: string[]) => void; | |
| } | |
| const MultiSelect = ({ | |
| title, | |
| options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'], | |
| value = [], | |
| onChange, | |
| }: MultiSelectProps) => { | |
| const selectedOptions = value; | |
| const toggleOption = (option: string) => { | |
| const newSelection = selectedOptions.includes(option) | |
| ? selectedOptions.filter((item) => item !== option) | |
| : [...selectedOptions, option]; | |
| onChange?.(newSelection); | |
| }; |
🤖 Prompt for AI Agents
In src/components/onboarding/MultiSelect.tsx around lines 3 to 18, the component
keeps selectedOptions internal and never exposes changes to the parent; add an
onChange prop to MultiSelectProps (e.g., onChange?: (selected: string[]) =>
void), accept it in the component signature, and invoke it whenever selection
changes (either call onChange(updatedSelected) inside toggleOption after
updating state or use a useEffect watching selectedOptions to call onChange).
Ensure the prop is properly typed and has no-op fallback if undefined.
| {/* 6개 그리드 버튼 */} | ||
| <div className="grid grid-cols-2 gap-4 md:grid-cols-3"> | ||
| {options.map((option, index) => ( | ||
| <button | ||
| key={index} | ||
| onClick={() => toggleOption(option)} | ||
| className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${ | ||
| selectedOptions.includes(option) | ||
| ? 'border-[#0D2D84] bg-[#0D2D84] text-white' | ||
| : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' | ||
| }`} | ||
| > | ||
| {option} | ||
| </button> | ||
| ))} | ||
| </div> |
There was a problem hiding this comment.
접근성 개선을 위해 버튼 상태 속성을 추가하세요.
선택 가능한 버튼들에 aria-pressed 속성이 없어 스크린 리더 사용자가 현재 선택 상태를 파악하기 어렵습니다.
다음과 같이 접근성 속성을 추가하세요:
<button
key={index}
onClick={() => toggleOption(option)}
+ aria-pressed={selectedOptions.includes(option)}
className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${
selectedOptions.includes(option)
? 'border-[#0D2D84] bg-[#0D2D84] text-white'
: 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50'
}`}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {/* 6개 그리드 버튼 */} | |
| <div className="grid grid-cols-2 gap-4 md:grid-cols-3"> | |
| {options.map((option, index) => ( | |
| <button | |
| key={index} | |
| onClick={() => toggleOption(option)} | |
| className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${ | |
| selectedOptions.includes(option) | |
| ? 'border-[#0D2D84] bg-[#0D2D84] text-white' | |
| : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' | |
| }`} | |
| > | |
| {option} | |
| </button> | |
| ))} | |
| </div> | |
| {/* 6개 그리드 버튼 */} | |
| <div className="grid grid-cols-2 gap-4 md:grid-cols-3"> | |
| {options.map((option, index) => ( | |
| <button | |
| key={index} | |
| onClick={() => toggleOption(option)} | |
| aria-pressed={selectedOptions.includes(option)} | |
| className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${ | |
| selectedOptions.includes(option) | |
| ? 'border-[#0D2D84] bg-[#0D2D84] text-white' | |
| : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' | |
| }`} | |
| > | |
| {option} | |
| </button> | |
| ))} | |
| </div> |
🤖 Prompt for AI Agents
In src/components/onboarding/MultiSelect.tsx around lines 24 to 39, the
selectable buttons are missing ARIA state which makes it hard for screen reader
users to know which options are selected; add an aria-pressed attribute to each
button bound to the selection state (e.g.,
aria-pressed={selectedOptions.includes(option)}) so the button exposes its
pressed/selected state, and also ensure the element is a proper button
(type="button") to avoid form submission side-effects.
Summary by CodeRabbit
릴리스 노트
새로운 기능
스타일
기타